/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package treegenerator.gui;

import TreeGenerator.Tools.CrossSection;
import TreeGenerator.Tools.CrossSectionGrid;
import com.jme3.bounding.BoundingBox;
import com.jme3.export.binary.BinaryExporter;
import com.jme3.gde.core.assets.ProjectAssetManager;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.VertexBuffer;
import com.jme3.scene.VertexBuffer.Format;
import com.jme3.scene.VertexBuffer.Type;
import com.jme3.scene.VertexBuffer.Usage;
import com.jme3.scene.mesh.IndexBuffer;
import com.jme3.util.BufferUtils;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import net.sourceforge.arbaro.mesh.LeafMesh;
import net.sourceforge.arbaro.tree.Tree;
import org.openide.filesystems.FileUtil;
import treegenerator.arbaro.ArbaroToJMonkey;

/**
 *
 * @author jannes
 */
public class TreePanel extends JPanel{
    private OffScenePanelWithMovement offScenePanel;
    private Node currentTree;
    private MainPanel mainPanel;
    private String treeName;
    private JLabel statusLabel;
    private JTextField txtSeed;
    private JButton btnExportTree,btnCreateTree;
    private ProjectAssetManager assetManager;
    private LeafMesh currentLeafMesh;
    
    public TreePanel(MainPanel mainPanel, ProjectAssetManager assetManager){
        super(new BorderLayout());
        this.mainPanel = mainPanel;
        this.assetManager = assetManager;
        JLabel lblTitle = new JLabel("Tree");
        lblTitle.setFont(new Font(lblTitle.getFont().getName(), Font.BOLD, 20));
        Box hb = Box.createHorizontalBox();        
        hb.add(Box.createHorizontalGlue());
        hb.add(lblTitle);
        hb.add(Box.createHorizontalGlue());
        add(hb,BorderLayout.NORTH);
        
        Box b = Box.createVerticalBox();                
        add(b,BorderLayout.CENTER);
                
        hb = Box.createHorizontalBox();        
        b.add(hb);
        hb.add(Box.createHorizontalGlue());
        hb.add(new JLabel("seed:"));
        txtSeed = new JTextField("13");
        txtSeed.setMaximumSize(new Dimension(50,25));
        txtSeed.setPreferredSize(new Dimension(50,25));
        hb.add(txtSeed);
        hb.add(Box.createHorizontalGlue());
        
        hb = Box.createHorizontalBox();        
        b.add(hb);
        hb.add(Box.createHorizontalGlue());        
        btnCreateTree = new JButton("Create tree");
        hb.add(btnCreateTree);
        hb.add(Box.createHorizontalGlue());        
        btnCreateTree.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                createTree();                
            }
        });  
        
        offScenePanel = new OffScenePanelWithMovement(500,800);                 
        offScenePanel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
        b.add(offScenePanel);
                
        btnExportTree = new JButton("Export tree");
        btnExportTree.setEnabled(false);
        b.add(btnExportTree);        
        btnExportTree.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                exportTree();
            }
        });          
        
        statusLabel = new JLabel("");
        add(statusLabel,BorderLayout.SOUTH);                        
        
    }
    
    public void dispose(){
        offScenePanel.stopPreview();
        offScenePanel.cleanup();
        offScenePanel = null;
        currentTree = null;
    }
    
    public String getTreeName(){
        return treeName;
    }
    
    public void setTreeName(String name){
        treeName = name;
    }
    
    private void createTree(){                   
        new Thread(new Runnable() {
            @Override
            public void run() {
                try{
                    btnCreateTree.setEnabled(false);
                    Long start  = System.nanoTime();                                                    
                    statusLabel.setText("Planting seed");
                    ArbaroToJMonkey treeCreator = new ArbaroToJMonkey(assetManager);  
                    statusLabel.setText("Creating tree structure");
                    Tree arbaroTree = treeCreator.createTree(mainPanel.getXMLViewer().getInputStream(),Integer.parseInt(txtSeed.getText()));                                                        
                    TexturePanel texturePanel = mainPanel.getTexturePanel();                
                    statusLabel.setText("Creating tree mesh");                    
                    currentTree = treeCreator.createMesh(arbaroTree, 1, texturePanel.getTrunkMaterial(assetManager), texturePanel.getStemMaterial(assetManager), texturePanel.getLeafMaterial(assetManager),texturePanel.getLeafSides(), texturePanel.getLeafSeed());
                    currentLeafMesh = treeCreator.getLeafMesh(arbaroTree);
                    statusLabel.setText("Optimizing tree mesh");
                    currentTree.updateModelBound();                             
                    
                    Long end = System.nanoTime();                        
                    statusLabel.setText("Loading tree into preview window");
                    offScenePanel.stopPreview();                    
                    offScenePanel.cleanup();                    
                    offScenePanel.attach(currentTree);        
                    offScenePanel.startPreview();                                     
                    offScenePanel.moveCamera(-20f);                
                    offScenePanel.doSetCamFocus(currentTree.getWorldBound().getCenter());                
                    
                    statusLabel.setText("time to create tree:" + ((double)(end-start))/1000000000); 
                    btnCreateTree.setEnabled(true);
                    btnExportTree.setEnabled(true);
                }catch(Exception exc){
                    JOptionPane.showMessageDialog(null, "error creating: " + exc.getMessage());
                }
            }
        }).start();
                
        
    }
    
    private void exportTree(){   
        try{
            File treeFile = new File(mainPanel.getProjectDirectory(), "assets/Models/Trees/" + treeName 
                 + "/" + txtSeed.getText() + "_"
                 + mainPanel.getTexturePanel().getSeeds() +  ".j3o");        
            treeFile.getParentFile().mkdirs();        
            exportTree(treeFile);
        }catch(Exception exc){
            StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter(sw);
            exc.printStackTrace(pw);
            JOptionPane.showMessageDialog(null, "error creating: " + sw.toString());
        }
    }
    
    private void exportTree(final File treeFile){
         new Thread(new Runnable() {
            @Override
            public void run() {
                try{
                    HashMap<String,String> textures = mainPanel.getTexturePanel().saveCurrentTextures(mainPanel.getProjectDirectory(),treeName);
                    for(String s: textures.keySet()){            
                        currentTree.setUserData(s, textures.get(s));
                    }  
                    File textureDir = new File(mainPanel.getProjectDirectory(),"assets/Textures/Trees/" + treeName );
                    
                    Spatial crossSection = CrossSectionGrid.CreateCrossSectionGrid(50, (Geometry)currentTree.getChild("leaves"), mainPanel.getRenderManager(), assetManager, textureDir, txtSeed.getText() + "_" + mainPanel.getTexturePanel().getSeeds(),2,(BoundingBox)currentTree.getChild("leaves").getWorldBound(),2);
                    crossSection.setName("CrossSection");         
                    currentTree.attachChild(crossSection);                        
                    //statusLabel.setText("starting creating crossections");
                    //addLeaveCrossSections((Geometry)currentTree.getChild("leaves"),4, textureDir);                    
                                        
                    BinaryExporter bi = new BinaryExporter();                
                    try {
                        bi.save(currentTree, treeFile);            
                        statusLabel.setText("Tree saved as: " + treeFile.getName());
                    } catch (IOException ex) {
                        StringWriter sw = new StringWriter();
                        PrintWriter pw = new PrintWriter(sw);
                        ex.printStackTrace(pw);
                        JOptionPane.showMessageDialog(null, "error creating: " + sw.toString());                        
                    }
                    FileUtil.toFileObject(treeFile).refresh();
                }catch(Exception exc){
                    StringWriter sw = new StringWriter();
                    PrintWriter pw = new PrintWriter(sw);
                    exc.printStackTrace(pw);
                    JOptionPane.showMessageDialog(null, "error creating: " + sw.toString());
                }
            }
        }).start();
    }        
    
    public void changeExportButton(final File file){
        for( ActionListener al : btnExportTree.getActionListeners()) {
            btnExportTree.removeActionListener( al );
        }
        btnExportTree.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                exportTree(file);
            }
        });          
    }
    
    public void addLeaveCrossSections(Geometry leaves, int rasterSize, File textureDir){        
        Vector3f min = new Vector3f();
        Vector3f max = new Vector3f();        
        ((BoundingBox)leaves.getWorldBound()).getMin(min);
        ((BoundingBox)leaves.getWorldBound()).getMax(max);
        float xDivider = (max.x - min.x)/rasterSize;        
        float yDivider = (max.y - min.y)/rasterSize;
        float zDivider = (max.z - min.z)/rasterSize;        
        int indexCount = currentLeafMesh.getShapeFaceCount()*3*2; //*2 for back and front faces        
        ArrayList<ArrayList<Integer>> lists = new ArrayList<ArrayList<Integer>>();
        for(int i =  0; i < rasterSize*rasterSize*rasterSize; i++){
            lists.add(new ArrayList<Integer>());
        }
        
        IndexBuffer ib = leaves.getMesh().getIndexBuffer();
        VertexBuffer buffer = leaves.getMesh().getBuffer(Type.Position);
        float[] vertexArray = BufferUtils.getFloatArray((FloatBuffer) buffer.getData()); 

        Vector3f[] minBounds = new Vector3f[rasterSize*rasterSize*rasterSize];
        for(int i = 0; i < minBounds.length; i++){
            minBounds[i] = new Vector3f(Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE);
        }
        Vector3f[] maxBounds = new Vector3f[rasterSize*rasterSize*rasterSize];
        for(int i = 0; i < maxBounds.length; i++){
            maxBounds[i] = new Vector3f(Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE);
        }
        
        for(int i = 0; i < ib.size(); i+=indexCount){            
            statusLabel.setText("creating lists: " + i + "/" + ib.size());
            float xlocation = vertexArray[ib.get(i)*3];
            float ylocation = vertexArray[ib.get(i)*3+1];
            float zlocation = vertexArray[ib.get(i)*3+2];
            int xIndex = (int)((xlocation-min.x)/xDivider);
            if(xIndex >= rasterSize){
                xIndex = rasterSize-1;
            }
            int yIndex = (int)((ylocation-min.y)/yDivider);
            if(yIndex >= rasterSize){
                yIndex = rasterSize-1;
            }
            int zIndex = (int)((zlocation-min.z)/zDivider);            
            if(zIndex >= rasterSize){
                zIndex = rasterSize-1;
            }
            int listIndex = xIndex + yIndex*rasterSize + zIndex*rasterSize*rasterSize;
            if(listIndex > lists.size()){
                JOptionPane.showMessageDialog(null, xIndex + ", " + yIndex + ", " + zIndex + ", " + listIndex);
            }
            for(int j = 0; j < indexCount; j++){
                lists.get(listIndex).add(ib.get(i+j));
                minBounds[listIndex].set(Math.min(xlocation, minBounds[listIndex].x), Math.min(ylocation, minBounds[listIndex].y), Math.min(zlocation, minBounds[listIndex].z));
                maxBounds[listIndex].set(Math.max(xlocation, maxBounds[listIndex].x), Math.max(ylocation, maxBounds[listIndex].y), Math.max(zlocation, maxBounds[listIndex].z));
            }
        }
        Geometry tempGeo;
        synchronized(leaves){
            tempGeo = (Geometry)leaves.deepClone();
            tempGeo.setName("tempGeo");
        }                
        for(int i = 0; i < lists.size(); i++){
        //for(int i = 0; i < 1; i++){
            tempGeo.removeFromParent();
            statusLabel.setText("creating crossections: " + (i+1) + "/" + lists.size());            
            ArrayList<Integer> indexList = lists.get(i);
            if(!indexList.isEmpty() && !maxBounds[i].equals(minBounds[i])){
                int[] indexes = new int[indexList.size()];
                for(int j = 0; j < indexes.length; j++){
                    indexes[j] = indexList.get(j);
                }                        
                tempGeo.getMesh().getBuffer(Type.Index).updateData(BufferUtils.createIntBuffer(indexes));                        
                leaves.getMesh().getBuffer(Type.Index).updateData(BufferUtils.createIntBuffer(indexes));
                VertexBuffer[] lods = new VertexBuffer[3];            
                lods[0] = new VertexBuffer(Type.Index);
                lods[0].setupData(Usage.Dynamic, 1, Format.UnsignedInt, BufferUtils.createIntBuffer(indexes));        
                tempGeo.getMesh().setLodLevels(lods);                    
                tempGeo.setLodLevel(0);  
                //tempGeo.setModelBound(new BoundingBox(minBounds[i], maxBounds[i]));
                Spatial crossSection = CrossSection.CreateCrossSection(50, tempGeo, mainPanel.getRenderManager(), assetManager, textureDir, txtSeed.getText() + "_" + mainPanel.getTexturePanel().getSeeds()+"_leafPart_" + i,2,new BoundingBox(minBounds[i], maxBounds[i]));            
                crossSection.setName("leafPart" + i);
                currentTree.attachChild(crossSection);
            }
        }   
    }    
}
